#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>

#include "pointoperation.h"
#include "utils.h"
#include <gmp.h>

#include "kdf.h"
#include "sm2.h"
#include "sm3.h"

void test_gmp();
void test_gmp_to_str();

void test_cal_elliptic_curve_point();

void test_point_init();
void test_point_add();
void test_point_mul();

void test_ecc();

void test_kdf();
void test_sm2_param();

void test_sm2();

int test_malloc();

int main()
{
	// test_gmp();

	// test_gmp_to_str();

	// test_cal_elliptic_curve_point();

	// test_point_init();

	// test_point_add();

	// test_point_mul();

	// test_ecc();

	// test_kdf();

	// test_sm2_param();

	test_sm2();

	// test_malloc();
}

void test_gmp()
{
	println("费马小定理求逆");
	// a、模数p、a的逆，临时变量
	mpz_t a, p, a_inv, temp;
	// 分配空间
	mpz_inits(a, p, a_inv, temp, NULL);
	mpz_set_ui(a, 6);
	mpz_set_ui(p, 23);

	// 求 a^-1
	inverse_by_p(a_inv, a, p);

	// temp = (a * a_inv) % p
	mpz_mul(temp, a, a_inv);
	mpz_mod(temp, temp, p);

	gmp_printf("a=%Zd，a^(-1)=%Zd，mode=%Zd，a(a^(-1))=%Zd\n", a, a_inv, p, temp);

	// 释放空间
	mpz_clears(a, p, a_inv, temp, NULL);
}

void test_gmp_to_str()
{
	mpz_t num;
	char *str_num = malloc(sizeof(char) * 20);

	// 初始化mpz_t变量
	mpz_init(num);

	// 假设我们有一个大数存储在num中，这里仅作为示例赋值
	mpz_set_ui(num, 123456); // 设置一个简单的数值

	// 将mpz_t转换为char*，基数为10
	str_num = mpz_get_str(str_num, 10, num);

	// 输出转换后的字符串
	printf("Number as string: %s\n", str_num);

	// 释放由mpz_get_str分配的内存
	free(str_num);

	size_t test_len = 10;
	char *test = malloc(sizeof(char) * test_len);

	mpz_to_fixed_length_str(test, 10, num, 16);

	free(test);

	// 清理mpz_t变量
	mpz_clear(num);
}

void test_cal_elliptic_curve_point()
{
	println("8.1.1 有限域上的椭圆曲线");
	// cal_elliptic_curve_point(1, 6, 11);

	cal_elliptic_curve_point(1, 1, 23);
}

void test_point_init()
{
	println("点初始化测试");
	// 定义了3个点
	point point1, point2, point3;

	// 使用可变参数函数对三个点进行初始化
	point_inits(point1, point2, point3, NULL);

	// 使用可变参数函数释放三个点的空间
	point_clears(point1, point2, point3, NULL);
}

void test_point_add()
{
	println("使用GMP库进行点加运算。其中椭圆曲线E23(1,1) ");
	// 定义变量，并进行初始化
	mpz_t a, b, p, temp;
	mpz_inits(a, b, p, temp, NULL);

	point point1, point2, point3;
	point_inits(point1, point2, point3, NULL);

	// 赋值
	mpz_set_ui(a, 1);
	mpz_set_ui(b, 1);
	mpz_set_ui(p, 23);

	mpz_set_ui(point1->x, 3);
	mpz_set_ui(point1->y, 10);

	mpz_set_ui(point2->x, 9);
	mpz_set_ui(point2->y, 7);

	// 点加运算 P + Q
	point_add(point3, point1, point2, a, p);
	gmp_printf("P1=(%Zd, %Zd)，P2=(%Zd, %Zd)，P3=P1+P2=(%Zd, %Zd)\n", point1->x, point1->y, point2->x, point2->y, point3->x, point3->y);

	// 点加运算 P + P
	point_add(point3, point1, point1, a, p);
	gmp_printf("P1=(%Zd, %Zd)，2P=(%Zd, %Zd)\n", point1->x, point1->y, point3->x, point3->y);

	// 释放空间
	point_clears(point1, point2, point3, NULL);
	mpz_clears(a, b, p, temp, NULL);
}

void test_point_mul()
{
	println("使用GMP库进行多倍点运算。其中椭圆曲线E11(1,6)");

	// 定义变量，并进行初始化
	mpz_t a, b, p, temp;
	mpz_inits(a, b, p, temp, NULL);

	point point1, point2, point3;
	point_inits(point1, point2, point3, NULL);

	// 赋值
	mpz_set_ui(a, 1);
	mpz_set_ui(b, 6);
	mpz_set_ui(p, 11);

	mpz_set_ui(point1->x, 2);
	mpz_set_ui(point1->y, 7);

	point_mul(point3, point1, b, a, p);
	gmp_printf("P1=(%Zd, %Zd)，%ZdP=(%Zd, %Zd)\n", point1->x, point1->y, b, point3->x, point3->y);

	// 验证生成元 point1
	for (int i = 1; i < 50; i++)
	{
		mpz_set_ui(temp, i);
		point_mul(point3, point1, temp, a, p);

		gmp_printf("P1=(%Zd, %Zd)，%ZdP=(%Zd, %Zd)\n", point1->x, point1->y, temp, point3->x, point3->y);
	}

	// 释放空间
	point_clears(point1, point2, point3, NULL);
	mpz_clears(a, b, p, temp, NULL);
}

void test_ecc()
{
	println("8.1.4 ECC");

	// 椭圆曲线的一次项系数a、二次项系数b和模数p
	mpz_t a, b, p;
	mpz_inits(a, b, p, NULL);

	// 生成元、公钥、密文1，密文2，信息, 恢复成的信息，临时变量
	point point1, public_key, ciphertext1, ciphertext2, message, recover_message, temp;
	point_inits(point1, public_key, ciphertext1, ciphertext2, message, recover_message, temp, NULL);

	// 变量初始化
	mpz_set_ui(a, 1);
	mpz_set_ui(b, 6);
	mpz_set_ui(p, 11);

	mpz_set_ui(point1->x, 2);
	mpz_set_ui(point1->y, 7);
	mpz_set_ui(message->x, 9);
	mpz_set_ui(message->y, 1);

	//	k是随机数，d是私钥
	mpz_t k, d;
	mpz_inits(k, d, NULL);
	mpz_set_ui(k, 6);
	mpz_set_ui(d, 7);

	// 测试 kd * G
	// mpz_mul(d, k, d);
	// point_mul(public_key, point1, d, a, p);
	// gmp_printf("k*d G=(%Zd, %Zd)\n", public_key->x, public_key->y);
	// mpz_set_ui(d, 7);

	// 1.计算公钥
	// public_key = d * point1
	point_mul(public_key, point1, d, a, p);
	gmp_printf("message = (%Zd, %Zd)\n", message->x, message->y);
	gmp_printf("P1 = (%Zd, %Zd)，public_key = %ZdP = (%Zd, %Zd)\n", point1->x, point1->y, d, public_key->x, public_key->y);

	// 2.加密
	// 密文1
	// ciphertext1 = k * point1
	point_mul(ciphertext1, point1, k, a, p);
	gmp_printf("P1 = (%Zd, %Zd)，ciphertext1 = %ZdP = (%Zd, %Zd)\n", point1->x, point1->y, k, ciphertext1->x, ciphertext1->y);

	// 密文2
	// ciphertext2 = k * public_key
	point_mul(ciphertext2, public_key, k, a, p);
	gmp_printf("k * public_key = %Zd * (%Zd, %Zd) = (%Zd, %Zd)\n", k, public_key->x, public_key->y, ciphertext2->x, ciphertext2->y);

	// ciphertext2 = message +  k * public_key
	point_add(ciphertext2, message, ciphertext2, a, p);
	gmp_printf("ciphertext2 = M + k * public_key = (%Zd, %Zd) + %Zd(%Zd, %Zd) = (%Zd, %Zd)\n", message->x, message->y, k, public_key->x, public_key->y, ciphertext2->x, ciphertext2->y);

	// 3.解密
	// temp = d * ciphertext1
	point_mul(temp, ciphertext1, d, a, p);
	// message = ciphertext2 - d * ciphertext1
	point_sub(ciphertext2, ciphertext2, temp, a, p);
	gmp_printf("recover message = (%Zd, %Zd)\n", ciphertext2->x, ciphertext2->y);

	// 4.释放空间
	mpz_clears(k, d, NULL);
	point_clears(point1, public_key, ciphertext1, ciphertext2, message, recover_message, temp, NULL);
	mpz_clears(a, b, p, NULL);
}

void test_kdf()
{
	println("测试密钥派生函数");

	size_t input_len = 48;
	uint8_t input[] = {0x57, 0xE7, 0xB6, 0x36,
					   0x23, 0xFA, 0xE5, 0xF0,
					   0x8C, 0xDA, 0x46, 0x8E,
					   0x87, 0x2A, 0x20, 0xAF,
					   0xA0, 0x3D, 0xED, 0x41,
					   0xBF, 0x14, 0x03, 0x77,

					   0x0E, 0x04, 0x0D, 0xC8,
					   0x3A, 0xF3, 0x1A, 0x67,
					   0x99, 0x1F, 0x2B, 0x01,
					   0xEB, 0xF9, 0xEF, 0xD8,
					   0x88, 0x1F, 0x0A, 0x04,
					   0x93, 0x00, 0x06, 0x03};

	// 如果需要验证国标 GBT 32918.4-2016 附录A 示例1，则 out_len 设置19（152/8=19）
	uint8_t out_len = 30;
	uint8_t out[30];

	kdf(out, out_len, input, input_len);
	print_chars_by_hex("KDF函数测试", out, out_len);
}

void test_sm2_param()
{
	param sm2_param;

	sm2_param_init(sm2_param);

	gmp_printf("p = %ZX\n", sm2_param->p);
	gmp_printf("p = %ZX\n", sm2_param->a);
	gmp_printf("p = %ZX\n", sm2_param->b);
	gmp_printf("p = %ZX\n", sm2_param->n);
}

void test_sm2()
{
	// 参数初始化
	param sm2_param;
	sm2_param_init(sm2_param);

	// 1.密钥生成
	// 进制基数
	int base = 16;
	// 私钥
	mpz_t private_key;
	mpz_init(private_key);
	// 58892B80 7074F53F BF67288A 1DFAA1AC 313455FE 60355AFD
	mpz_set_str(private_key, "58892B807074F53FBF67288A1DFAA1AC313455FE60355AFD", base);

	// 基点G、公钥
	point base_point, public_key;
	point_inits(base_point, public_key, NULL);

	// 4AD5F704 8DE709AD 51236DE6 5E4D4B48 2C836DC6 E4106640
	mpz_set_str(base_point->x, "4AD5F7048DE709AD51236DE65E4D4B482C836DC6E4106640", base);

	// 02BB3A02 D4AAADAC AE24817A 4CA3A1B0 14B52704 32DB27D2
	mpz_set_str(base_point->y, "02BB3A02D4AAADACAE24817A4CA3A1B014B5270432DB27D2", base);

	// 根据私钥生成公钥
	public_key_gen(public_key, private_key, sm2_param, base_point);

	gmp_printf("x = %ZX\n", public_key->x);
	gmp_printf("y = %ZX\n", public_key->y);

	// 2.SM2加密
	uint8_t msg[] = "encryption standard abcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
	// 注意：这里包含了结尾标志 "\0"
	size_t msg_len = sizeof(msg);

	size_t ct_len = msg_len + 1 + 24 * 2 + 32;
	uint8_t *ct = malloc(sizeof(uint8_t) * ct_len);
	ct[0] = '\0';

	printf("明文：%s\n", msg);
	sm2_encrypt(ct, ct_len, sm2_param, public_key, base_point, msg, msg_len);

	print_chars_by_hex("SM2加密结果", ct + 1, ct_len - 1);

	// 3.SM2解密
	uint8_t *plaintext = malloc(sizeof(uint8_t) * msg_len);
	sm2_decrypt(plaintext, msg_len, sm2_param, private_key, ct, ct_len);
	print_chars_by_hex("SM2解密结果", plaintext, msg_len);
	printf("SM2解密结果：%s\n", plaintext);
}

int test_malloc()
{
	uint8_t *temp = (uint8_t *)malloc(1000000 * sizeof(uint8_t));
	if (temp == NULL)
	{
		fprintf(stderr, "Memory allocation failed.\n");
		return EXIT_FAILURE;
	}

	// 假设temp_len是一个未初始化的变量
	size_t temp_len = 100;
	for (size_t i = 0; i < temp_len; i++)
	{
		temp[i] = 0;
	}

	// 使用temp...

	free(temp);

	return 0;
}